import types  # 使用模块中的类型常量
# 根据对象引用查询其类型、属性、方法等 type()
print(type(None))
print(type(abs))
print(type('str') == str)  # 基本类型
# 判断函数类型


def function_type_test():
    pass


print(types.FunctionType)
# 判断class的类型，可以使用isinstance()函数。
print(isinstance(function_type_test, object))
print(isinstance('str', str))
print(isinstance('str', (str, int)))  # 第一个参数的对象是否是第二个参数的类型，第二个参数可以填写多个
# 获得一个对象的所有属性和方法，可以使用dir()函数，它返回一个包含字符串的list。
print(dir(str))
print(len('ABC'))
print('ABC'.__len__())
# 自定义的类，如果也想用len(Obj)的话，需要自己写一个__len__()方法


class Dog(object):
    def __init__(self, name, age):
        self.name = name
        self.__age = age

    def __len__(self):
        return 10

    def run(self):
        return 'running!'


d = Dog('Husky', 18)
print(len(d))
# 其他方法
print('测试大写字母转小写方法：' + 'ABC'.lower())
# 配合getattr()、setattr()以及hasattr()，我们可以直接操作一个对象的状态
d2 = Dog('DogKing', 8)
print(d2.name)
print('获取存在的属性name：' + getattr(d2, 'name'))
print(hasattr(d2, 'age'))
setattr(d2, 'color', 'red')
print('获取添加的新属性color值：' + getattr(d2, 'color'))
print('获取添加的新属性color值：' + d2.color)
mov = getattr(d2, 'run')  # 获取对象的方法
print(mov())
print('获取不存在的属性：' + getattr(d2, 'type', '404'))

# 小结
# 通过内置的一系列函数，我们可以对任意一个Python对象进行剖析，拿到其内部的数据。
# 要注意的是，只有在不知道对象信息的时候，我们才会去获取对象信息。如果可以直接写： sum = obj.x + obj.y
# 就不要写：sum = getattr(obj, 'x') + getattr(obj, 'y')
# 一个正确的用法的例子如下：
# def readImage(fp):
#     if hasattr(fp, 'read'):
#         return readData(fp)
#     return None
# 假设我们希望从文件流fp中读取图像，我们首先要判断该fp对象是否存在read方法，
# 如果存在，则该对象是一个流，如果不存在，则无法读取。hasattr()就派上了用场。
# 请注意，在Python这类动态语言中，根据鸭子类型，有read()方法，不代表该fp对象就是一个文件流，它也可能是网络流，
# 也可能是内存中的一个字节流，但只要read()方法返回的是有效的图像数据，就不影响读取图像的功能。
